<script>

(function() {

  var modules = {};
  var lcModules = {};
  var findModule = function(id) {
    return modules[id] || lcModules[id.toLowerCase()];
  };

  /**
   * The `dom-module` element registers the dom it contains to the name given
   * by the module's id attribute. It provides a unified database of dom
   * accessible via any dom-module element. Use the `import(id, selector)`
   * method to locate dom within this database. For example,
   *
   * <dom-module id="foo">
   *   <img src="stuff.png">
   * </dom-module>
   *
   * Then in code in some other location that cannot access the dom-module above
   *
   * var img = document.createElement('dom-module').import('foo', 'img');
   *
   */
  var DomModule = function() {
    return document.createElement('dom-module');
  };

  DomModule.prototype = Object.create(HTMLElement.prototype);

  Polymer.Base.mixin(DomModule.prototype, {

    createdCallback: function() {
      this.register();
    },

    /**
     * Registers the dom-module at a given id. This method should only be called
     * when a dom-module is imperatively created. For
     * example, `document.createElement('dom-module').register('foo')`.
     * @method register
     * @param {String} id The id at which to register the dom-module.
     */
    register: function(id) {
      id = id || this.id ||
        this.getAttribute('name') || this.getAttribute('is');
      if (id) {
        this.id = id;
        // store id separate from lowercased id so that
        // in all cases mixedCase id will stored distinctly
        // and lowercase version is a fallback
        modules[id] = this;
        lcModules[id.toLowerCase()] = this;
      }
    },

    /**
     * Retrieves the dom specified by `selector` in the module specified by
     * `id`. For example, this.import('foo', 'img');
     * @method register
     * @param {String} id
     * @param {String} selector
     * @return {Object} Returns the dom which matches `selector` in the module
     * at the specified `id`.
     */
    import: function(id, selector) {
      if (id) {
        var m = findModule(id);
        if (!m) {
          // If polyfilling, a script can run before a dom-module element
          // is upgraded. We force the containing document to upgrade
          // dom-modules and try again to workaround this polyfill limitation.
          forceDomModulesUpgrade();
          m = findModule(id);
        }
        if (m && selector) {
          m = m.querySelector(selector);
        }
        return m;
      }
    }

  });

  Object.defineProperty(DomModule.prototype, 'constructor', {
    value: DomModule, configurable: true, writable: true
  });

  // NOTE: HTMLImports polyfill does not
  // block scripts on upgrading elements. However, we want to ensure that
  // any dom-module in the tree is available prior to a subsequent script
  // processing.
  // Therefore, we force any dom-modules in the tree to upgrade when dom-module
  // is registered by temporarily setting CE polyfill to crawl the entire
  // imports tree. (Note: this should only upgrade any imports that have been
  // loaded by this point. In addition the HTMLImports polyfill should be
  // changed to upgrade elements prior to running any scripts.)
  var cePolyfill = window.CustomElements && !CustomElements.useNative;
  // NOTE: Under polyfilled CE/HI, if script and html are separate, then
  // for dom modules to be found, script should be executed as follows:
  //   HTMLImports.whenReady(function() {
  //    CustomElements.ready = false;
  //    // registrations
  //    CustomElements.upgradeDocumentTree(document);
  //    CustomElements.ready = true;
  //  });
  // TODO(sorvell): A webcomponentsjs method should be added for this.
  document.registerElement('dom-module', DomModule);

  function forceDomModulesUpgrade() {
    if (cePolyfill) {
      var script = document._currentScript || document.currentScript;
      var doc = script && script.ownerDocument || document;
      // find all dom-modules
      var modules = doc.querySelectorAll('dom-module');
      // minimize work by going backwards and stopping if we find an
      // upgraded module.
      for (var i= modules.length-1, m; (i >=0) && (m=modules[i]); i--) {
        if (m.__upgraded__) {
          return;
        } else {
          CustomElements.upgrade(m);
        }
      }
    }
  }

})();

</script>
